// module eMIPS_top(
module mycpu_top(
    input clk, 
    input resetn,
    input int,

    output inst_sram_en,
    output [3:0] inst_sram_wen,
    output [31:0] inst_sram_addr,
    output [31:0] inst_sram_wdata,
    input [31:0] inst_sram_rdata,

    output data_sram_en,
    output [3:0] data_sram_wen,
    output [31:0] data_sram_addr,
    output [31:0] data_sram_wdata,
    input [31:0] data_sram_rdata,

    output [31:0] debug_wb_pc,
    output [3:0] debug_wb_rf_wen,
    output [4:0] debug_wb_rf_wnum,
    output [31:0] debug_wb_rf_wdata

);
    wire [1:0] IF_PCSrc_in;
    wire [31:0] IF_nextPC_in;
    // wire IF_flush_in;
    wire [31:0] IF_Inst_out ;
    wire [31:0] IF_PC_out, IF_PC4_out;
    wire IF_inst_sram_en_out;
    wire [3:0] IF_inst_sram_wen_out;
    wire [31:0] IF_inst_sram_addr_out;
    wire [31:0] IF_inst_sram_wdata_out = 0;
    wire [31:0] IF_inst_sram_rdata_in;
    wire IF_IF_addr_fault_out;
    
    wire IF_stall_out;

    assign inst_sram_en = IF_inst_sram_en_out;
    assign inst_sram_addr = IF_inst_sram_addr_out;
    assign inst_sram_wdata = IF_inst_sram_wdata_out;
    assign IF_inst_sram_rdata_in = inst_sram_rdata;

    IF IF_U(.clk(clk), 
        .rst_n(resetn), 
        .PCSrc(IF_PCSrc_in),
        .nextPC(IF_nextPC_in), 
        // .flush(IF_flush_in), 
        .Inst(IF_Inst_out),
        .PC(IF_PC_out), 
        .PC4(IF_PC4_out),
        .IF_stall(IF_stall_out),

        .inst_sram_en(IF_inst_sram_en_out),
        .inst_sram_wen(IF_inst_sram_wen_out), 
        .inst_sram_addr(IF_inst_sram_addr_out),
        // .inst_sram_wdata(IF_inst_sram_wdata_out), 
        .inst_sram_rdata(IF_inst_sram_rdata_in),
        
        .IF_addr_fault(IF_IF_addr_fault_out)
    );

    wire IFID_flush_in;
    
    // ID input 
    wire [31:0] ID_PC_in;
    wire [31:0] ID_PC4_in;
    wire [31:0] ID_Inst_in;
    wire [4:0] ID_write_dst_in;
    // wire [31:0] ID_write_dst__in;
    wire ID_write_reg_in;
    wire [31:0] ID_write_reg_data_in;
    wire ID_write_epc_in;
    wire [31:0] ID_write_epc_data_in;
    wire ID_write_cp0reg_in;
    wire ID_flush_in;
    wire ID_IF_stall_in;
    wire ID_IF_addr_fault_in;
    // ID out put 
    wire [31:0] ID_PC_out;
    wire [31:0] ID_PC4_out;
    wire [4:0] ID_write_dst_out;
    wire ID_write_reg_out;
    wire ID_write_cp0reg_out;
    wire [31:0] ID_nextPC_out;
    wire [31:0] ID_reg_data1_out;
    wire [31:0] ID_reg_data2_out;
    wire [31:0] ID_extImm_out;
    wire [31:0] ID_Inst_out;
    wire [1:0] ID_write_hilo_out;
    wire [3:0] ID_write_mem_out;
    wire ID_trap_out;
    wire [2:0] ID_extOp_out;
    wire [3:0] ID_write_data_src_out;
    wire [2:0] ID_aluOp_out;
    wire [31:0] ID_cp0_reg_out;
    wire [4:0] ID_sa_out;
    wire ID_isbranch_out;
    wire [3:0] ID_data_sram_wen_out;
    wire [31:0] ID_data_sram_addr_out;
    wire ID_addrSrc_out;
    wire [31:0] ID_epc_data_out;
    wire ID_IF_addr_fault_out;
    wire ID_delay_slot_out;
    wire ID_flush_out;
    wire ID_ri_fault_out;

    IF_ID IF_ID_U(.clk(clk), 
        .rst_n(resetn), 
        .PC_in(IF_PC_out), 
        .PC4_in(IF_PC4_out), 
        .Inst_in(IF_Inst_out), 
        .flush(IFID_flush_in),
        .IF_stall_in(IF_stall_out),
        .IF_addr_fault_in(IF_IF_addr_fault_out),

        .PC_out(ID_PC_in), 
        .PC4_out(ID_PC4_in), 
        .Inst_out(ID_Inst_in),
        .IF_stall_out(ID_IF_stall_in),
        .IF_addr_fault_out(ID_IF_addr_fault_in)
    );

    ID ID_U(.clk(clk), 
        .rst_n(resetn), 
        .PC_in(ID_PC_in), 
        .PC4_in(ID_PC4_in),
        .Inst_in(ID_Inst_in), 
        .write_dst_in(ID_write_dst_in), 
        .write_reg_in(ID_write_reg_in),
        .write_reg_data(ID_write_reg_data_in), 
        .write_epc(ID_write_epc_in), 
        .write_epc_data(ID_write_epc_data_in), 
        .write_cp0reg_in(ID_write_cp0reg_in), 
        .flush(ID_flush_in), 
        .IF_stall(ID_IF_stall_in),
        .IF_addr_fault_in(ID_IF_addr_fault_in),

        .PC_out(ID_PC_out), 
        .PC4_out(ID_PC4_out), 
        .write_dst_out(ID_write_dst_out),
        .write_reg_out(ID_write_reg_out), 
        .write_cp0reg_out(ID_write_cp0reg_out), 
        .nextPC(ID_nextPC_out),
        .reg_data1(ID_reg_data1_out),
        .reg_data2(ID_reg_data2_out),
        .extImm(ID_extImm_out),
        .Inst_out(ID_Inst_out),
        .write_hilo(ID_write_hilo_out),
        .write_mem(ID_write_mem_out),
        .trap(ID_trap_out),
        .extOp(ID_extOp_out),
        .write_data_src(ID_write_data_src_out),
        .aluOp(ID_aluOp_out),
        .cp0_reg(ID_cp0_reg_out),
        .sa(ID_sa_out),
        .isbranch(ID_isbranch_out),
        .data_sram_wen(ID_data_sram_wen_out),
        .data_sram_addr(ID_data_sram_addr_out),
        .addrSrc(ID_addrSrc_out),
        .epc_data(ID_epc_data_out),
        .IF_addr_fault_out(ID_IF_addr_fault_out),
        .delay_slot_out(ID_delay_slot_out),
        .ID_flush(ID_flush_out),
        .ri_fault(ID_ri_fault_out)
    );

    // EMW input 
    wire [31:0] EX_PC_in;
    wire [31:0] EX_PC4_in;
    wire [4:0] EX_write_dst_in;
    wire EX_write_reg_in;
    wire EX_write_cp0reg_in;
    wire [31:0] EX_reg_data1_in;
    wire [31:0] EX_reg_data2_in;
    wire [31:0] EX_extImm_in;
    wire [31:0] EX_Inst_in;
    wire [1:0] EX_write_hilo_in;
    wire [3:0] EX_write_mem_in;
    wire EX_trap_in;
    wire [2:0] EX_extOp_in;
    wire [3:0] EX_write_data_src_in;
    wire [2:0] EX_aluOp_in;
    wire [31:0] EX_cp0_reg_in;
    wire [4:0] EX_sa_in;
    wire [31:0] EX_data_sram_rdata_in = data_sram_rdata;
    wire EX_flush_in;
    wire [31:0] EX_nextPC_in;
    wire EX_isbranch_in;
    wire [3:0] EX_data_sram_wen_in;
    wire [31:0] EX_epc_data_in;
    wire EX_IF_addr_fault_in;
    wire EX_delay_slot_in;
    wire EX_ID_flush_in;
    wire EX_ri_fault_in;
    // EMW out put 
    wire EX_data_sram_en_out;
    wire [3:0] EX_data_sram_wen_out;
    wire [31:0] EX_data_sram_addr_out;
    wire [31:0] EX_data_sram_wdata_out;
    wire EX_exception_out;
    wire [31:0] EX_write_data_out;
    wire [31:0] EX_epc_out;
    wire EX_write_reg_out;
    wire EX_write_epc_out;
    wire EX_write_cp0reg_out;
    wire [4:0] EX_write_dst_out;
    wire [31:0] EX_nextPC_out;
    wire EX_isbranch_out;

    ID_EX ID_EX_U(
        .clk(clk),
        .rst_n(resetn),

        .PC_in(ID_PC_out),
        .PC4_in(ID_PC4_out),
        .write_dst_in(ID_write_dst_out),
        .write_reg_in(ID_write_reg_out),
        .write_cp0reg_in(ID_write_cp0reg_out),
        .reg_data1_in(ID_reg_data1_out),
        .reg_data2_in(ID_reg_data2_out),
        .extImm_in(ID_extImm_out),
        .Inst_in(ID_Inst_out),
        .write_hilo_in(ID_write_hilo_out),
        .write_mem_in(ID_write_mem_out),
        .trap_in(ID_trap_out),
        .extOp_in(ID_extOp_out),
        .write_data_src_in(ID_write_data_src_out),
        .aluOp_in(ID_aluOp_out),
        .cp0_reg_in(ID_cp0_reg_out),
        .sa_in(ID_sa_out),
        .nextPC_in(ID_nextPC_out),
        .isbranch_in(ID_isbranch_out),
        .data_sram_wen_in(ID_data_sram_wen_out),
        .epc_data_in(ID_epc_data_out),
        .IF_addr_fault_in(ID_IF_addr_fault_out),
        .delay_slot_in(ID_delay_slot_out),
        .ID_flush_in(ID_flush_out),
        .ri_fault_in(ID_ri_fault_out),
        // .EX_flush(EX_flush)

        .PC_out(EX_PC_in),
        .PC4_out(EX_PC4_in),
        .write_dst_out(EX_write_dst_in),
        .write_reg_out(EX_write_reg_in),
        .write_cp0reg_out(EX_write_cp0reg_in),
        .reg_data1_out(EX_reg_data1_in),
        .reg_data2_out(EX_reg_data2_in),
        .extImm_out(EX_extImm_in),
        .Inst_out(EX_Inst_in),
        .write_hilo_out(EX_write_hilo_in),
        .write_mem_out(EX_write_mem_in),
        .trap_out(EX_trap_in),
        .extOp_out(EX_extOp_in),
        .write_data_src_out(EX_write_data_src_in),
        .aluOp_out(EX_aluOp_in),
        .sa_out(EX_sa_in),
        .nextPC_out(EX_nextPC_in),
        .isbranch_out(EX_isbranch_in),
        .data_sram_wen_out(EX_data_sram_wen_in),
        .epc_data_out(EX_epc_data_in),
        .IF_addr_fault_out(EX_IF_addr_fault_in),
        .delay_slot_out(EX_delay_slot_in),
        .ID_flush_out(EX_ID_flush_in),
        .ri_fault_out(EX_ri_fault_in)
    );

    wire DM_Interface_data_sram_en_out;
    wire [3:0] DM_Interface_data_sram_wen_out;
    wire [31:0] DM_Interface_data_sram_addr_out;
    wire [31:0] DM_Interface_data_sram_wdata_out;

    DM_Interface DM_Interface_U(
        .data_sram_wen_in(EX_data_sram_wen_out),
        .ID_data_sram_addr(ID_data_sram_addr_out),
        .EX_data_sram_addr(EX_data_sram_addr_out),
        .addrSrc(ID_addrSrc_out),
        .data_sram_wdata_in(EX_data_sram_wdata_out),
        .data_sram_rdata_in(data_sram_rdata),
        
        .data_sram_en(DM_Interface_data_sram_en_out),
        .data_sram_wen_out(DM_Interface_data_sram_wen_out),
        .data_sram_addr(DM_Interface_data_sram_addr_out),
        .data_sram_wdata_out(DM_Interface_data_sram_wdata_out),
        .data_sram_rdata_out(EX_data_sram_rdata_in)
    );

    EMW EMW_U(
        .clk(clk),
        .rst_n(resetn),
        
        .PC(EX_PC_in),
        .PC4(EX_PC4_in),
        .write_dst_in(EX_write_dst_in),
        .write_reg_in(EX_write_reg_in),
        .write_cp0reg_in(EX_write_cp0reg_in),
        .reg_data1(EX_reg_data1_in),
        .reg_data2(EX_reg_data2_in),
        .extImm(EX_extImm_in),
        .Inst(EX_Inst_in),
        .write_hilo(EX_write_hilo_in),
        .write_mem(EX_write_mem_in),
        .trap(EX_trap_in),
        .extOp(EX_extOp_in),
        .write_data_src(EX_write_data_src_in),
        .aluOp(EX_aluOp_in),
        .cp0_reg(EX_cp0_reg_in),
        .sa(EX_sa_in),
        .data_sram_rdata(EX_data_sram_rdata_in),
        .flush(EX_flush_in),
        .nextPC_in(EX_nextPC_in),
        .isbranch_in(EX_isbranch_in),
        .data_sram_wen_in(EX_data_sram_wen_in),
        .epc_data(EX_epc_data_in),
        .IF_addr_fault(EX_IF_addr_fault_in),
        .delay_slot(EX_delay_slot_in),
        .ID_flush(EX_ID_flush_in),
        .ri_fault(EX_ri_fault_in),

        // .data_sram_en(EX_data_sram_en_out),
        .data_sram_wen_out(EX_data_sram_wen_out),
        .data_sram_addr(EX_data_sram_addr_out),
        .data_sram_wdata(EX_data_sram_wdata_out),
        .exception(EX_exception_out),
        .write_data(EX_write_data_out),
        .epc(EX_epc_out),
        .write_reg_out(EX_write_reg_out),
        .write_epc(EX_write_epc_out),
        .write_cp0reg_out(EX_write_cp0reg_out),
        .write_dst_out(EX_write_dst_out),
        .nextPC_out(EX_nextPC_out),
        .isbranch_out(EX_isbranch_out)
    );
    // assign data_sram_en = EX_data_sram_en_out;
    // assign data_sram_wen = EX_data_sram_wen_out;
    // assign data_sram_addr = EX_data_sram_addr_out;
    // assign data_sram_wdata = EX_data_sram_wdata_out;
    // assign  EX_data_sram_
    // assign 
    assign data_sram_en = DM_Interface_data_sram_en_out;
    assign data_sram_wen = DM_Interface_data_sram_wen_out;
    // assign data_sram_addr = DM_Interface_data_sram_addr_out;
    assign data_sram_addr = ( DM_Interface_data_sram_addr_out[31:28] == 4'ha 
        || DM_Interface_data_sram_addr_out[31:28] == 4'hb ) ? DM_Interface_data_sram_addr_out - 32'ha0000000 : DM_Interface_data_sram_addr_out;
    assign data_sram_wdata = DM_Interface_data_sram_wdata_out;

    assign ID_write_dst_in = EX_write_dst_out;
    assign ID_write_reg_in = EX_write_reg_out;
    assign ID_write_reg_data_in = EX_write_data_out;
    assign ID_write_epc_in = EX_write_epc_out;
    assign ID_write_epc_data_in = EX_epc_out;
    assign ID_write_cp0reg_in = EX_write_cp0reg_out;
    // assign ID_write_epc_in = EX_write_p

    ControlHazard ControlHazard_U(
        .PC(IF_PC_out),
        .nextPC_in(EX_nextPC_out),
        .exception(EX_exception_out),
        .isbranch(EX_isbranch_out),
        .EX_Inst(EX_Inst_in),

        .nextPC_out(IF_nextPC_in),
        .IF_flush(IFID_flush_in),
        .ID_flush(ID_flush_in),
        .EX_flush(EX_flush_in),
        .PCSrc(IF_PCSrc_in)
    );


    // debug 
    assign debug_wb_pc = EX_PC_in;
    assign debug_wb_rf_wen = {4{EX_write_reg_out}};
    assign debug_wb_rf_wnum = EX_write_dst_out;
    assign debug_wb_rf_wdata = EX_write_data_out;

endmodule // eMIPS_top